home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 10 - 1994 / 10.05 May 94 / Programmer’s Challenge / bitmap.c
Encoding:
C/C++ Source or Header  |  1994-03-11  |  10.0 KB  |  363 lines  |  [TEXT/KAHL]

  1. /* March 94 Challenge - BitMapToText
  2. ** by Bob Boonstra
  3. **
  4. ** Strategy
  5. **
  6. **   The problem states that "the smallest detail in the
  7. **     input image [will] be roughly equal to or larger than
  8. **     a single character of the given font and font size.
  9. **   Therefore, this solution attempts only to match the
  10. **     number of bits set in a given character size piece
  11. **     of the image with the number of bits set in the
  12. **     character chosen to represent that piece of the 
  13. **     image.
  14. **   The strategy is to:
  15. **     (1) draw the characters from 32 to 127 in an 
  16. **         offscreen bitmap.
  17. **     (2) sort the characters in order of increasing 
  18. **         number of bits set
  19. **     (3) precalculate a mapping from pixel density to
  20. **         output characters
  21. **     (4) loop thru the character size chunks of the image,
  22. **         count the number of bits set, and output the
  23. **         corresponding character.
  24. **
  25. ** Assumptions
  26. **   Width of characters is assumed to be <=32 pixels
  27. **     (reasonable for (6-24 point mono font)
  28. **   No assumption that actual height of font <= 24;
  29. **     ascent+descent+leading may exceed point size
  30. **     Ref NIM: Text pg 4-11
  31. **   bitMapPtr->rowBytes * (font height) assumed < 32K
  32. */
  33.  
  34. #include <stdio.h>
  35.  
  36. #pragma options (honor_register, !assign_registers)
  37.  
  38. #define uchar unsigned char
  39. #define ushort unsigned short
  40. #define ulong unsigned long
  41.  
  42. #define EOL 0x0d
  43. #define kErr 1
  44. #define kFirstChar 32
  45. #define kLastCharPlus1 128
  46. #define kNumChars (kLastCharPlus1-kFirstChar)
  47. #define kBytesPerBMChar sizeof(long)
  48. #define kMaxCharWidth (8*kBytesPerBMChar)
  49. #define kCharRowBytes 384
  50. #define kBitsPerChunk 32
  51. #define kMaxCharVals 512
  52.  
  53. #define DoSetMem(addr,sz,val)                            \
  54.   { register long *p = (long *)addr;                     \
  55.     register short count = sz;                           \
  56.     do *p++=val; while (--count);                        \
  57.   }
  58.  
  59. // Macro BitCount(x,count) increments count for each bit 
  60. //   in x set to 1.
  61. // WARNING:  the expression (x=x--&x) in the BitCount 
  62. //   macro is not portable, because the order of evaluation
  63. //   is undefined, but it generates correct fast code
  64. //   for (x=x&(x-1)) in THINK C.
  65. //   Non-portable code is BAD FOR YOU, except where speed
  66. //   is very important, like in this Challenge.
  67. #define BitCount(x,initVal,count)  \
  68.   if (x=initVal) do ++count; while (x = x--&x);
  69.  
  70. ushort lineHeight,charWid;
  71. FontInfo fInfo;
  72.  
  73. short InitOffscreenBitMap(GrafPort *charPtr)
  74. {
  75. //
  76. // Initialize font information
  77. //
  78.     Point scalePt = {1,1};
  79.     ulong numBytes;
  80.     GetFontInfo(&fInfo);
  81.     lineHeight = fInfo.leading+fInfo.ascent+fInfo.descent;
  82.     charPtr->pnLoc.v = lineHeight -fInfo.descent;
  83. //
  84. // Initialize GrafPort and bitmap storage 
  85. //
  86.     numBytes = lineHeight*kCharRowBytes;
  87.     if (0 == (charPtr->portBits.baseAddr = 
  88.                 (QDPtr)NewPtr( numBytes )))
  89.       return kErr;
  90.     DoSetMem(charPtr->portBits.baseAddr,
  91.                                   numBytes/sizeof(long),0);
  92.     charPtr->portBits.rowBytes = kCharRowBytes;
  93.     charPtr->portBits.bounds.top = 0;
  94.     charPtr->portBits.bounds.left = 0;
  95.     charPtr->portBits.bounds.bottom = lineHeight;
  96.     charPtr->portBits.bounds.right = kCharRowBytes*8;
  97.     RectRgn(charPtr->visRgn,&charPtr->portBits.bounds);
  98.     charWid = StdTxMeas(1,"W",&scalePt,&scalePt,&fInfo);
  99. /*if (charWid != fInfo.widMax) DebugStr("\p bad wid");*/
  100.     if (kBytesPerBMChar*8 < charWid) return kErr;
  101.     return 0;
  102.   }
  103.  
  104. //
  105. // Draw the characters of the given font into an offscreen
  106. // bitmap.
  107. //
  108. void  DrawTheChars(GrafPtr charPort)
  109.   register Point scalePt = {1,1};
  110.   register short hPos = kMaxCharWidth-charWid;
  111.   register short count;
  112.   uchar chVal = kFirstChar;
  113.   count = kNumChars; do {
  114.     charPort->pnLoc.h = hPos;
  115.     StdText(1,&chVal,scalePt,scalePt);
  116.     hPos += kMaxCharWidth;
  117.     ++chVal;
  118.   } while (--count);
  119. }
  120.  
  121. //
  122. // Calculate the number of bits set in each character, for 
  123. // subsequent use in comparing to a section of the bitmap.
  124. //
  125. void InitBitsSetArray(register char *p,register ushort *c)
  126. {
  127.   register short count;
  128.   p += (ushort)fInfo.leading*kCharRowBytes;
  129.   count = kNumChars; do {
  130.     register ushort bitcount=0;
  131.     register uchar *q = (uchar *)p;
  132.     register short vCount;
  133.     vCount = lineHeight-fInfo.leading; do {
  134.       register ulong row;       
  135.       BitCount(row,*(ulong *)q,bitcount);
  136.       q += kCharRowBytes;
  137.     } while (--vCount);
  138. //  Following line fudges the density value for characters
  139. //  to account for the fact that the most dense character
  140. //  is significantly less dense than a dark section of a
  141. //  bitmap.
  142.     bitcount += bitcount>>1;
  143.     *c++ = bitcount;
  144.     p += kBytesPerBMChar;
  145.   } while (--count);
  146. }
  147.  
  148. //
  149. // Sort the characters in order of increasing number of
  150. // bits set (density).
  151. //
  152. void SortBitsSetArray(register ushort *v, 
  153.                       register ushort *c)
  154. {
  155.   register ushort *x;
  156.   register ushort count,val,xVal,newVal;
  157. // Initialize sort order
  158.   count = kNumChars; val = 0; x=c; do {
  159.     *x++ = val;  ++val;
  160.   } while (--count);
  161. // Bidirectional exchange sort is good enough for this small
  162. // array
  163.   count = kNumChars-1;
  164.   x = c;
  165.   val = *(v+*c);
  166.   do {
  167.     ushort *saveC;
  168.     ushort saveCount;
  169.     xVal = *(c+1);
  170.     newVal = *(v+xVal);
  171.     if (val > /**x*/ newVal) {
  172. //    Swap pointers
  173.       *(c+1) = *c;   *c = xVal;
  174.       if (count < kNumChars-1) {
  175.         saveC=c+1;  saveCount=count;  val = *(v+*c); 
  176.         do {
  177.           xVal = *(c-1);  x = v+xVal;  newVal = *x;
  178.           if (val >= newVal) break;
  179.           *(c-1) = *c;  *c = xVal;   --c;
  180.         } while (++count < kNumChars-1);
  181.         count = saveCount;  c=saveC;  val = *(v+*c);
  182.       } else {
  183.         val = *(v+*c++);
  184.       }
  185.     } else {
  186.       val = newVal;  ++c;
  187.     }
  188.   } while (--count);
  189. }
  190.  
  191. //
  192. // Initialize a mapping from number of bits set in a
  193. // character-sized section of the bitmap to the character
  194. // used to represent that section.
  195. //
  196. void InitCharPointerArray(register ushort *v, 
  197.      register ushort *c, register ushort *p)
  198. {
  199.   register short count1,count2,count3;
  200.   register short currentVal;
  201.   count2 = kNumChars;
  202.   count1 = -1;
  203.   do {
  204.     currentVal = *(v+*c);
  205.     if (currentVal>count1) {
  206.       count3 = (ushort)(currentVal-count1)/2;
  207.       if (count3) do {
  208.         *p++=*(p-1);
  209.         if (++count1 >= kMaxCharVals) return;
  210.       } while (--count3);
  211.       do {
  212.         *p++=' '+*c;
  213.         if (++count1 >= kMaxCharVals) return;
  214.       } while (currentVal>count1);
  215.     }
  216.     ++c;
  217.   } while (--count2);
  218.   do {
  219.     *p++=*(p-1);
  220.   } while (++count1<kMaxCharVals);
  221. }
  222.  
  223. short BitMapToText(bitMapPtr,fontName,fontSize,outputFile)
  224. BitMap *bitMapPtr;
  225. Str255 fontName;
  226. unsigned short fontSize;
  227. FILE *outputFile;
  228. {
  229. GrafPort charPort;
  230. GrafPtr savePort;
  231.  
  232. //
  233. // bitsSet[x] is the number of bits set to 1 in the 
  234. //            representation of character ' '+x
  235. // sortedCharP[y]+' ' is the y-th character in order of
  236. //                    increasing number of bits set
  237. //
  238. ushort bitsSet[kNumChars],sortedCharP[kNumChars];
  239. //
  240. // charVals[c] is the character to be output for a character
  241. //             size piece of the bitmap with c bits set
  242. //
  243. ushort charVals[1+kMaxCharVals];
  244.  
  245. register ulong *q,*rowP;
  246. register short count,numBitsSet;
  247. register ulong mask;
  248.  
  249. register uchar *p;
  250. ulong origMask;
  251. ushort lineBytes;
  252. short rCnt,rowBytes,hPix,fontNum,theErr;
  253.  
  254.   GetFNum(fontName,&fontNum);
  255.   if (0 == fontNum) return (kErr);
  256.   if (!RealFont(fontNum,fontSize)) return (kErr);
  257.   GetPort(&savePort);
  258.   OpenPort(&charPort);
  259.   TextFont(fontNum);
  260.   TextSize(fontSize);
  261.   if (theErr = InitOffscreenBitMap(&charPort))
  262.     return theErr;
  263. //
  264. // Draw characters in bitmap.  Draws them one at a time
  265. // so we can align the characters within long words. 
  266. //
  267.   DrawTheChars(&charPort);
  268.   SetPort(savePort);
  269. //  
  270. // Init charVal array of characters to output.
  271. //
  272.   InitBitsSetArray(charPort.portBits.baseAddr,bitsSet);
  273.   SortBitsSetArray(bitsSet,sortedCharP);
  274.   InitCharPointerArray(bitsSet,sortedCharP,charVals);
  275. //
  276. //  Process bitMap.
  277. //
  278.   p = (uchar *)bitMapPtr->baseAddr;
  279.   rowBytes = bitMapPtr->rowBytes;
  280.   lineBytes = rowBytes*lineHeight;
  281.   rCnt = bitMapPtr->bounds.bottom - bitMapPtr->bounds.top;
  282.   hPix = bitMapPtr->bounds.right - bitMapPtr->bounds.left;
  283. //
  284. // Set a mask of charWid characters using a sign-extended
  285. // shift.
  286. //
  287.   origMask = mask = (ulong)
  288.            ((signed long)0x80000000>>(charWid-1));
  289. //
  290. //  Loop on rows of characters.
  291. //
  292.   do {
  293.     short numBits,bitsThisChunk,bitsToGo,hCnt;
  294.     rowP = (ulong *)p;
  295.     bitsThisChunk = kBitsPerChunk;
  296.     hCnt = hPix;
  297.     numBits = bitsToGo = charWid;
  298. //
  299. //  Loop on chars within current row.
  300. //
  301.     do {
  302. //
  303. //    Count bits set in current char.
  304. //
  305.       numBitsSet=0;
  306. //
  307. //    Loop on pixels in this chunk.
  308. //
  309.       do {
  310.         q = rowP;
  311.         count = lineHeight;
  312. //
  313. //      Count number of bits set in this chunk.
  314. //
  315.         do {
  316.           register ulong ch;
  317.           BitCount(ch,*q & mask,numBitsSet);
  318.           q = (ulong *)((uchar *)q + rowBytes);
  319.         } while (--count);
  320. //
  321. //  Continue processing current char if there are bitsToGo.
  322. //
  323.         if (0 == (bitsThisChunk -= numBits)) {
  324. //        Check for end of row.
  325.           if (hCnt < (bitsThisChunk=kBitsPerChunk))
  326.             bitsThisChunk = hCnt;
  327.           ++rowP;
  328.           if (bitsToGo-=numBits) {
  329.             if (bitsToGo > hCnt) bitsToGo = hCnt;
  330.             mask = origMask << (charWid - bitsToGo);
  331.             numBits = bitsToGo;
  332.           } else {
  333.             mask = origMask;
  334.             break;
  335.           }
  336.         } else if (numBits != charWid) {
  337.           mask = (origMask >> bitsToGo);
  338.           break;
  339.         } else {
  340.           mask >>= numBits;
  341.           break;
  342.         }
  343.       } while (true);  /* break if (0 == bitsToGo) */
  344.       numBits = bitsToGo = charWid;
  345.       if (numBits>bitsThisChunk) numBits=bitsThisChunk;
  346. //
  347. //  Select output character;
  348. //
  349.       if (numBitsSet<kMaxCharVals)
  350.         count = *(charVals+numBitsSet);
  351.       else count = *(charVals+kMaxCharVals);
  352.       putc(count,outputFile);
  353.     } while (0 < (hCnt-=charWid));
  354.     p += lineBytes;
  355.     putc(EOL,outputFile);
  356.     if (0 > rCnt) break;
  357.     if (0 > (rCnt-=lineHeight)) lineHeight += rCnt;
  358.     mask =  origMask;
  359.   } while (true);
  360.   return 0;
  361. }
  362.